<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>聊天室</title>
		<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
	    <style>
		    /* 设置滚动条的样式 */
		    ::-webkit-scrollbar {
			    width:5px;
		    }
		    /* 滚动槽 */
		    ::-webkit-scrollbar-track {
			    -webkit-box-shadow:inset 0 0 6px rgba(0,0,0,0.3);
			    border-radius:10px;
		    }
		    /* 滚动条滑块 */
		    ::-webkit-scrollbar-thumb {
			    border-radius:10px;
			    background:rgba(0,0,0,0.1);
			    -webkit-box-shadow:inset 0 0 6px rgba(72, 192, 164,0.5);
		    }
		    ::-webkit-scrollbar-thumb:window-inactive {
			    background:rgba(72, 192, 164,0.4);
		    }
		    ::placeholder {
			    color: #79879a; /* 文字颜色 */
		    }

		    /*预定义样式，通过js动态生成dom时，加上指定类名*/
		    .dpn-message {font-family: "\5FAE\8F6F\96C5\9ED1", Helvetica, sans-serif;font-size: 18px;z-index: 99999;}
		    .dpn-message {box-sizing: border-box;position: fixed;top: -200px;left: 50%;transform: translateX(-50%);z-index: 99999;padding: 20px;padding-right: 32px;min-width: 25%;max-width: 50%;border-radius: 4px;transition: top .3s;}
		    /*info 消息*/
		    .dpn-message.dpn-info {background: #EDF2FC;border: 1px solid #EBEEF5;color: #909399;}
		    /*success消息*/
		    .dpn-message.dpn-success {background: #f0f9eb;border: 1px solid #54f006;color: #67C23A;}
		    /*error消息*/
		    .dpn-message.dpn-error {background: #fef0f0;border: 1px solid #fde2e2;color: #F56C6C;}
		    /*warning消息*/
		    .dpn-message.dpn-warning {background: #fdf6ec;border: 1px solid #faecd8;color: #E6A23C;}
		    .dpn-message .dpn-close {position: absolute;right: 8px;top: 50%;transform: translateY(-50%);width: 16px;height: 16px;line-height: 16px;text-align: center;font-style: normal;cursor: pointer;}
	    </style>
        <style>
	        body{font-family: Arial, sans-serif;margin: 0;padding: 0;background-color: #282C34;background-position: center;background-repeat: no-repeat;background-attachment: fixed;background-size:100%;}
	        .main {display: flex;justify-content: space-between;margin: 20px;}
	        .public-channel,
	        .private-channel {flex: 1;border: 1px solid #68768a;border-radius: 5px;padding: 10px;box-sizing: border-box;margin-right: 10px;}
	        .public-channel{margin-right: 80px;}
	        h3 {font-size: 20px;color: #48c0a4;}
	        .top {display: flex;align-items: center;margin-bottom: 10px;}
	        #sendUser,
	        #receUser {
		        width: 300px;
		        height: 38px;
		        margin-right: 10px;
		        border: 1px solid rgba(0,0,0,0.2);
		        background: rgba(53,75,105,0.5);
		        padding: 5px 10px;
		        border-radius: 5px;
		        font-size: 16px;
		        color: #cfe2f3;
		        outline: none;
	        }
	        #头像box {
		        display: flex;
		        align-items: center;
		        width: 50px;
		        height: 50px;
		        cursor: pointer;
		        font-size: 14px;
	        }
	        #fileImg {display: none;vertical-align: middle;}
	        #img {max-width: 50px;}
	        #fileBox {
		        position: relative;
		        display: inline-block;
		        width: 50px;
		        height: 50px;
		        line-height: 50px;
		        border: 1px solid rgba(0,0,0,0.2);
		        background: rgba(53,75,105,0.5);
		        border-radius: 5px;
		        color: gold;
		        text-align: center;
		        cursor: pointer;
	        }
	        #file {
		        position: absolute;
		        opacity: 0;
		        width: 50px;
		        height: 50px;
		        cursor: pointer;
		        padding: 0;
		        margin-left: -12px;
	        }
	        button {
		        width: 16%;
		        height: 50px;
		        padding: 10px 20px;
		        color: gold;
		        background: rgba(53,75,105,0.5);
		        border-radius: 5px;
		        border: none;
		        cursor: pointer;
		        margin-left: 20px;
		        outline: none;
	        }
	        button:hover{color: darkorange;}
	        button:active {border: none;color: white;outline: none;background: LightSkyBlue;}

	        .msgBox {
		        border: 1px solid rgba(0,0,0,0.2);
		        padding: 10px;
		        height: 600px;
		        margin-bottom: 15px;
		        border-radius: 15px;
		        overflow: auto;
	        }
	        #publicMsg,
	        #privateMsg{display: flex;flex-direction: column;}
			.msg{display: flex;margin: 15px 5px 10px 5px;}
	        .right {margin-left: auto;}
	        .msg .pic-box{float: left;width:50px;height:50px;line-height:50px;text-align: center;}
	        .left .info-box{margin-left: 10px;}
	        .right .info-box{text-align: right;margin-right: 10px;}
	        .msg .msg-pic{color: deepskyblue;display: block;font-weight: bold;font-size: 20px;width:50px;vertical-align: middle;}
			.msg .msg-name{color: gold;font-size: 14px;margin-bottom: 1px;}
			.msg .msg-name1{font-weight: bold;color: #f75c2f;}
			.msg .msg-name2{font-weight: bold;}
			.msg .msg-time{color: #cfe2f3;font-size: 12px;}
			.msg .msg-msginfo{font-size: 15px;color: #cfe2f3;display: block;margin-top: 3px;word-break: break-all;}

	        textarea {
		        width: 97.5%;
		        height: 60px;
		        border: 1px solid rgba(0,0,0,0.2);
		        background: rgba(53,75,105,0.5);
		        padding: 10px;
		        outline: none;
		        border-radius: 15px;
		        font-size: 16px;
		        color: #cfe2f3;
	        }
        </style>
    </head>

    <body>
        <div class="main">
	        <div class="public-channel">
		        <h3>公共频道</h3>
		        <div class="top">
			        <input type="text" id="sendUser" placeholder="登录用户名">
			        <div id="头像box">
				        <div id="fileImg"><img src="" id="img" width="50"></div>
				        <div id="fileBox">
					        <input type="file" id="file" class="file" multiple="multiple" accept="image/jpeg,image/png,image/jpg">
					        <span style="cursor: pointer;">头像</span>
				        </div>
			        </div>
			        <button id="上线" onclick="connectWebSocket()">登录</button>
			        <button id="下线" onclick="closeWebSocket()">退出</button>
		        </div>
		        <div class="msgBox">
			        <div id="publicMsg">
				        <!--<div class="msg left">
					        <div class="pic-box">
						        <img src="img/符华.jpg" width="50" class="msg-pic">
					        </div>
					        <div class="info-box">
						        <span class="msg-name">一名路过的假面骑士</span>
						        <span class="msg-time">2023-10-27 10:25:08</span>
						        <span class="msg-msginfo">哈喽，大家好呀哈喽</span>
					        </div>
				        </div>
				        <div class="msg right">
					        <div class="info-box">
						        <span class="msg-time">2023-10-27 10:25:08</span>
						        <span class="msg-name">一名路过的假面骑士</span>
						        <span class="msg-msginfo">哈哈哈</span>
					        </div>
					        <div class="pic-box">
						        <img src="img/符华.jpg" width="50" class="msg-pic">
					        </div>
				        </div>-->
			        </div>
		        </div>
		        <div class="buttom">
			        <textarea type="text" id="msg1" placeholder="按Enter发送消息（Shift + Enter 换行）"></textarea>
		        </div>
	        </div>
	        <div class="private-channel">
		        <h3>私聊频道</h3>
		        <div class="top">
		            <input type="text" id="receUser" placeholder="私聊对象用户名"/>
		        </div>
		        <div class="msgBox">
			        <div id="privateMsg">
				        <!--<div class="msg left">
					        <div class="pic-box">
						        <span class="msg-pic">头像</span>
					        </div>
					        <div class="info-box">
						        <span class="msg-name msg-name2">一名路过的假面骑士</span>
						        <span class="msg-time">发给</span>
						        <span class="msg-name msg-name1">我</span>
						        <span class="msg-time">2023-10-27 10:26:08</span>
						        <span class="msg-msginfo">哈哈哈哈哈</span>
					        </div>
				        </div>
				        <div class="msg right">
					        <div class="info-box">
						        <span class="msg-time">2023-10-27 10:26:08</span>
						        <span class="msg-name msg-name2">我</span>
						        <span class="msg-time">发给</span>
						        <span class="msg-name msg-name1">一名路过的假面骑士</span>
						        <span class="msg-msginfo">哈哈哈哈哈</span>
					        </div>
					        <div class="pic-box">
						        <span class="msg-pic">头像</span>
					        </div>
				        </div>-->
			        </div>
		        </div>
		        <div class="buttom">
			        <textarea type="text" id="msg2" placeholder="按Enter发送消息（Shift + Enter 换行）"></textarea>
		        </div>
	        </div>
        </div>

        <script>
	        class MessageBox {
		        constructor(options) {
			        for(let key in options) {
				        if(!options.hasOwnProperty(key)) break;
				        this[key] = options[key];
			        }
			        this.init();
		        }
		        init() {if(this.status === "message") {this.createMessage();this.open();return;}}
		        createMessage() {
			        this.messageBox = document.createElement('div');
			        this.messageBox.className = `dpn-message dpn-${this.type}`;
			        this.messageBox.innerHTML = `${this.message}<i class="dpn-close">X</i>`;
			        document.body.appendChild(this.messageBox);
			        this.messageBox.onclick = ev => {
				        let target = ev.target;
				        if(target.className === "dpn-close") {this.close();}
			        };
			        this.oninit();
		        }
		        open() {
			        if(this.status === "message") {
				        let messageBoxs = document.querySelectorAll('.dpn-message'),len = messageBoxs.length;
				        this.messageBox.style.top = `${len===1 ? 20:20+(len-1)*70}px`;
				        this.autoTimer = setTimeout(() => {this.close();}, this.duration);
				        this.onopen();
				        return;
			        }
		        }
		        close() {
			        if(this.status === "message") {
				        clearTimeout(this.autoTimer);
				        this.messageBox.style.top = '-200px';
				        let anonymous = () => {document.body.removeChild(this.messageBox);this.onclose();};
				        this.messageBox.addEventListener('transitionend', anonymous);
				        return;
			        }
		        }
	        }
	        window.messageplugin = function(options = {}) {
		        if(typeof options === "string") {options = { message: options };}
		        options = Object.assign({status: 'message', message: '我是默认信息', type: 'info', duration: 1000, oninit() {}, onopen() {}, onclose() {},}, options);
		        return new MessageBox(options);
	        };
        </script>

        <script>
	        let file = document.getElementById('file'); // 选择文件
	        let fileBox = document.getElementById('fileBox'); // 选择文件box
	        let img = document.getElementById('img'); // 头像img标签
	        let fileImg = document.getElementById('fileImg'); // 头像img标签box
	        let textareaMsg1 = document.getElementById("msg1"); // 获取多行文本框元素
	        let textareaMsg2 = document.getElementById("msg2"); // 获取多行文本框元素
	        let imgBase64 = "",fileSuffix = "";
	        file.onchange = function (e){
		        if (e.target.files.length>0){
                    var selectedImage = e.target.files[0];
                    var fileSize = selectedImage.size / 1024; // 转换为KB
                    if (fileSize > 1024) {messageplugin({ message: "文件大小不得超过1MB", type: "error" });return;}
			        var name = selectedImage.name;
			        fileSuffix = name.slice(name.lastIndexOf(".")).replace("e",""); // 获取文件后缀
			        img.src = getFileURL(selectedImage);
			        fileImg.style.display = "inline-block";
			        fileBox.style.display = "none";
			        var fileReader = new FileReader();
			        fileReader.readAsDataURL(selectedImage); // 文件读取为url
			        fileReader.onload = function(e) {
				        imgBase64 = e.target.result; // 获取头像的base64
			        }
		        }
	        }
	        //获取文件地址
	        function getFileURL(file) {
		        var url = null ;
		        if (window.createObjectURL!=undefined) { // basic
			        url = window.createObjectURL(file) ;
		        } else if (window.URL!=undefined) { // mozilla(firefox)
			        url = window.URL.createObjectURL(file) ;
		        } else if (window.webkitURL!=undefined) { // webkit or chrome
			        url = window.webkitURL.createObjectURL(file) ;
		        }
		        return url;
	        }

	        // 定义两个模板字符串，使用占位符 ${} 来表示待填充的位置
	        // 公共频道其他用户的消息的模板（位于左边）
	        const publicLeftTemplate = `
				<div class="msg left">
					<div class="pic-box">
						  {{ImgHtmlSnippet}}
					</div>
					<div class="info-box">
						<span class="msg-name">${"{{SendUser}}"}</span>
						<span class="msg-time">${"{{SendTime}}"}</span>
						<span class="msg-msginfo">${"{{Msg}}"}</span>
					</div>
				</div>
			`;
	        // 公共频道我的消息的模板（位于右边）
	        const publicRightTemplate = `
				<div class="msg right">
					<div class="info-box">
						<span class="msg-time">${"{{SendTime}}"}</span>
						<span class="msg-name">${"{{SendUser}}"}</span>
						<span class="msg-msginfo">${"{{Msg}}"}</span>
					</div>
					<div class="pic-box">
						  {{ImgHtmlSnippet}}
					</div>
				</div>
			`;
	        // 私聊频道其他用户的消息的模板
	        const privateLeftTemplate = `
				<div class="msg left">
					<div class="pic-box">
						{{ImgHtmlSnippet}}
					</div>
					<div class="info-box">
						<span class="msg-name msg-name2">${"{{SendUser}}"}</span>
						<span class="msg-time">发给</span>
						<span class="msg-name msg-name1">${"{{ReceUser}}"}</span>
						<span class="msg-time">${"{{SendTime}}"}</span>
						<span class="msg-msginfo">${"{{Msg}}"}</span>
					</div>
				</div>
			`;
	        // 私聊频道我的消息的模板
	        const privateRightTemplate = `
				<div class="msg right">
					<div class="info-box">
						<span class="msg-time">${"{{SendTime}}"}</span>
						<span class="msg-name msg-name2">${"{{SendUser}}"}</span>
						<span class="msg-time">发给</span>
						<span class="msg-name msg-name1">${"{{ReceUser}}"}</span>
						<span class="msg-msginfo">${"{{Msg}}"}</span>
					</div>
					<div class="pic-box">
						{{ImgHtmlSnippet}}
					</div>
				</div>
			`;
	        // 填充模板
	        function filledTemplate(obj){
				console.log(obj);
		        var sendUser = document.getElementById("sendUser").value; // 当前用户
		        var isPublic = obj["IsPublic"];
		        // 头像代码段
		        const imgHtmlSnippet = obj["Pic"] != "" ? `<img src='${obj["Pic"]}' width="50" class="msg-pic">` : `<span class="msg-pic">${obj["PicName"]}</span>`;
		        var filledTemplate;
		        if (isPublic){
			        msgList = document.getElementById("publicMsg");
			        var template = publicLeftTemplate;
			        if (obj["SendUser"] == sendUser || obj["SendUser"] == "我"){
				        template = publicRightTemplate;
			        }
			        // 替换模板中的占位符
			        filledTemplate = template.replace("{{ImgHtmlSnippet}}", imgHtmlSnippet).replace(/\{\{(\w+)\}\}/g, (match, key) => {
				        if (key == "SendUser" && !obj["IsSend"]){
					        return "";
				        }else if (key == "SendUser" && obj["IsSend"]){
					        return "&nbsp;"+obj[key];
				        }
				        return obj[key] || "";
			        });
		        }else {
			        msgList = document.getElementById("privateMsg");
			        var template = privateLeftTemplate;
			        if (obj["SendUser"] == "我"){
				        template = privateRightTemplate;
			        }
			        // 替换模板中的占位符
			        filledTemplate = template.replace("{{ImgHtmlSnippet}}", imgHtmlSnippet).replace(/\{\{(\w+)\}\}/g, (match, key) => {
				        if (key == "ReceUser" && obj[key] == sendUser){ // 当前用户是接收人时
					        return "我";
				        }
				        return obj[key] || "";
			        });
		        }
		        // 将生成的 HTML 插入到页面中
		        msgList.innerHTML += filledTemplate;
	        }
        </script>

        <script type="text/javascript">
	        var websocket = null;
            //连接WebSocket
            function connectWebSocket() {
                var sendUser = document.getElementById("sendUser").value;
                if (sendUser === "") {messageplugin({ message: "请输入用户名", type: "error" });return;}
                //判断当前浏览器是否支持websocket
                if ('WebSocket' in window) {
	                var val = document.getElementById("sendUser").value;
                    // websocket = new WebSocket("ws://localhost:7070/web-socket/"+val);
                    // websocket = new WebSocket("ws://localhost:7070/web-socket?username="+val);
	                var pic = imgBase64;
	                var jsonData = {Name: val, Pic: pic, PicName: "",};  // 准备要发送的JSON数据
	                var jsonStr = JSON.stringify(jsonData); // 将JSON数据转换为字符串
	                websocket = new WebSocket(`ws://localhost:7070/web-socket?data=${encodeURIComponent(jsonStr)}`);
                } else {messageplugin({ message: "当前浏览器不支持 websocket", type: "error" });}
                //连接发生错误的回调方法
                websocket.onerror = function () {messageplugin({ message: "连接发生错误", type: "error" });};
                //连接成功建立的回调方法
                websocket.onopen = function () {
					// 连接成功后，将连接的用户输入框和上线按钮禁用
                    var sendUser = document.getElementById("sendUser");
                    var 上线 = document.getElementById("上线");
	                sendUser.readOnly = true;
					sendUser.disabled = "disabled";
	                sendUser.style.backgroundColor='rgba(0,0,0,0.2)';
                    file.style.display = "none";
                    fileBox.style.backgroundColor = "rgba(0,0,0,0.2)";
	                // 上线.removeAttribute("onclick");
	                上线.disabled = "disabled";
	                上线.style.backgroundColor='rgba(0,0,0,0.2)';
                }
                //接收到消息的回调方法
                websocket.onmessage = function (event) {
	                try {
		                var obj = JSON.parse(event.data); // 将服务器发过来的消息，转为json格式，捕获异常
		                filledTemplate(obj); // 填充模板
	                } catch (error) {
		                messageplugin({ message: event.data, type: "error" }); // 如果不是json格式则直接用 alert 提示
	                }
                }
                //连接关闭的回调方法
                websocket.onclose = function () {messageplugin({ message: "连接已关闭", type: "warning" });}
                //监听窗口关闭事件，当窗口关闭时，主动去关闭websocket连接，防止连接还没断开就关闭窗口，server端会抛异常。
                window.onbeforeunload = function () {closewebsocket();}
            }

            //关闭连接
            function closeWebSocket() {websocket.close();}

	        // 公共频道发送消息（监听键盘enter事件）
	        textareaMsg1.addEventListener("keydown", function(event) {
		        if (event.key === "Enter" && !event.shiftKey) { // 按下enter且没有按住shift键的情况下
			        event.preventDefault(); // 阻止默认的换行行为
			        send(textareaMsg1.value,true);
			        textareaMsg1.value = ""; // 清空消息
		        }
	        });

	        // 私聊频道发送消息（监听键盘enter事件）
	        textareaMsg2.addEventListener("keydown", function(event) {
		        if (event.key === "Enter" && !event.shiftKey) { // 按下enter且没有按住shift键的情况下
			        event.preventDefault(); // 阻止默认的换行行为
			        var receUser = document.getElementById("receUser").value; //接收者
			        if (receUser === "") {messageplugin({ message: "私聊对象不能为空", type: "error" });return;}
			        send(textareaMsg2.value,false);
			        textareaMsg2.value = ""; // 清空消息
		        }
	        });

            //发送消息
            function send(msg,isPublic) {
                var m = new Map(); // 空Map
                var sendUser = document.getElementById("sendUser");  //发送者
                if (msg === "") {messageplugin({ message: "不能发送空消息", type: "error" });return;}
                var receUser = document.getElementById("receUser").value; //接收者
	            var currentTime = getCurrentTime();
                m.set("SendUser",sendUser.value);
                m.set("SendTime",currentTime);
	            m.set("Msg",msg);
	            m.set("IsSend",true);
	            m.set("IsPublic",isPublic);
				if (!isPublic){
					if (receUser === "") {messageplugin({ message: "私聊对象不能为空", type: "error" });return;}
					if (receUser === sendUser.value){messageplugin({ message: "私聊对象不能是自己", type: "error" });return;}
					m.set("ReceUser",receUser);
				}
	            m.set("Pic",fileSuffix !== "" ?  "app/static/img/"+sendUser.value + fileSuffix : "");
	            m.set("PicName",sendUser.value.slice(0,2));
                var json = mapToJson(m); // map转json
                websocket.send(JSON.stringify(json)); // 先将json转json字符串，再发送
	            json["SendUser"] = "我";
	            filledTemplate(json);
            }

			// 获取当前时间
			function getCurrentTime(){
				//可以使用字符串操作方法来将日期时间格式化为特定格式的字符串。例如：
				const date = new Date();
				const year = date.getFullYear().toString().padStart(4, '0');
				const month = (date.getMonth() + 1).toString().padStart(2, '0');
				const day = date.getDate().toString().padStart(2, '0');
				const hour = date.getHours().toString().padStart(2, '0');
				const minute = date.getMinutes().toString().padStart(2, '0');
				const second = date.getSeconds().toString().padStart(2, '0');
				return `${year}-${month}-${day} ${hour}:${minute}:${second}`; // 2023-02-16 08:25:05
			}

            //map转换为json
            function  mapToJson(map) {
                var obj= Object.create(null);
                for (var[k,v] of map) { obj[k] = v; }
                return obj;
            }
        </script>
    </body>
</html>
